/*!
 * @file        apm32f10x_spi.c
 *
 * @brief         This file provides all the SPI firmware functions
 *
 * @details    
 *
 * @version        V1.0.0
 *
 * @date        2019-8-6
 *
 */
#include "apm32f10x_spi.h"
#include "apm32f10x_rcm.h"

/*!
 * @brief        Reset the specified SPI peripheral
 *
 * @param        spi: SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_I2S_Reset(SPI_T* spi)
{
    if(spi == SPI1)
    {
        RCM_EnableAPB2PeriphReset(RCM_APB2_PERIPH_SPI1);
        RCM_DisableAPB2PeriphReset(RCM_APB2_PERIPH_SPI1);
    }
    else if(spi == SPI2)
    {
        RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_SPI2);
        RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_SPI2);
    }
    else if(spi == SPI3)
    {
        RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_SPI3);
        RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_SPI3);
    }
}

/*!
 * @brief        Config the SPI peripheral according to the specified parameters in the configStruct
 *
 * @param        spi: SPI peripheral
 *
 * @param        configStruct: pointer to a SPI_ConfigStruct_T structure
 *
 * @retval        None
 *
 * @note 
 */
void SPI_Config(SPI_T* spi, SPI_ConfigStruct_T* configStruct)
{
    spi->CTRL1_B.CLKPHA = BIT_RESET;
    spi->CTRL1_B.CLKPOL = BIT_RESET;
    spi->CTRL1_B.MSTMODE = BIT_RESET;
    spi->CTRL1_B.BRC = BIT_RESET;
    spi->CTRL1_B.LSBF = BIT_RESET;
    spi->CTRL1_B.ISS = BIT_RESET;
    spi->CTRL1_B.SSC = BIT_RESET;
    spi->CTRL1_B.UMRXO = BIT_RESET;
    spi->CTRL1_B.BMTX = BIT_RESET;
    spi->CTRL1_B.BMEN = BIT_RESET;
    spi->CTRL1 |= (uint16_t)((uint32_t)configStruct->direction | configStruct->mode |
                  configStruct->dataSize | configStruct->cpol |  
                  configStruct->cpha | configStruct->nss |  
                  configStruct->baudRatePrescaler | configStruct->firstBit);
    
    spi->CRCPOLY = configStruct->crcPolynomial;
    
}




/*!
 * @brief        Config the I2S peripheral according to the specified parameters in the configStruct
 *
 * @param        spi: SPI peripheral
 *
 * @param        configStruct: pointer to a I2S_ConfigStruct_T structure
 *
 * @retval        None
 *
 * @note 
 */
void I2S_Config(SPI_T* spi, I2S_ConfigStruct_T* configStruct)
{
	uint16_t i2sdiv = 2, i2sodd = 0, packetlength = 1;
	uint32_t tmp = 0;
	uint32_t sourceclock = 0;

	/*----------------------- SPIx I2SCTRL & I2SDIV Configuration -----------------*/
  /* Clear I2SMOD, I2SEN, I2SCFMODE, PCMSYNC, I2SSTD, CLKPOL, DATALEN and CHLEN bits */
	spi->I2SCTRL &= 0xF040;
	spi->I2SDIV = 0x0002;
	
	/* If the default value has to be written, reinitialize i2sdiv and i2sodd*/
	if(configStruct->audioFreq == I2S_AUDIO_FREQUENCY_Default)
	{
		spi->I2SDIV_B.ODD = 0;
		spi->I2SDIV_B.I2SDIV = 2;
	}
	else
	{
		/* Check the frame length (For the Prescaler computing) */
		if(configStruct->dataFormat == I2S_DATAFORMAT_16b)
		{
			/* Packet length is 16 bits */
			packetlength = 1;
		}
		else
		{
			packetlength = 2;
		}
		
		/* Get the I2S clock source mask depending on the peripheral number */
		if(((uint32_t)spi) == SPI2_BASE)
		{
			/* The mask is relative to I2S2 */
			tmp = 0x00020000;
		}
		else 
		{
			/* The mask is relative to I2S3 */
			tmp = 0x00040000;
		}
		
		sourceclock = RCM_GetSYSCLKFreq();
		
		/* Compute the Real divider depending on the MCLK output state with a floating point */
		if(configStruct->MCLKOutput == I2S_MCLK_OUTPUT_ENABLE)
		{
			/* MCLK output is enabled */
			tmp = (uint16_t)(((((sourceclock / 256) * 10) / configStruct ->audioFreq)) + 5);
		}
		else 
		{
			tmp = (uint16_t)(((((sourceclock / (32 * packetlength)) *10 ) / configStruct ->audioFreq )) + 5);
		}
		tmp = tmp / 10;
		
		/* Check the parity of the divider */
		i2sodd = (uint16_t)(tmp & (uint16_t)0x0001);
		
    /* Compute the i2sdiv prescaler */
    i2sdiv = (uint16_t)((tmp - i2sodd) / 2);
   
    /* Get the Mask for the Odd bit (SPI_I2SPR[8]) register */
    i2sodd = (uint16_t) (i2sodd << 8);

		/* Test if the divider is 1 or 0 or greater than 0xFF */
		if ((i2sdiv < 2) || (i2sdiv > 0xFF))
		{
			/* Set the default values */
			i2sdiv = 2;
			i2sodd = 0;
		}
		
		spi->I2SDIV_B.I2SDIV = i2sdiv;
		spi->I2SDIV_B.ODD = i2sodd;
		spi->I2SDIV_B.MCKOEN = configStruct->MCLKOutput;
		
		spi->I2SCTRL = 0x0800 | configStruct->mode | configStruct->standard | configStruct->dataFormat | configStruct->CPOL;
	}
}

/*!
 * @brief        Fills each SPI_ConfigStruct_T member with its default value
 *
 * @param        configStruct: pointer to a SPI_ConfigStruct_T structure which will be initialized
 *
 * @retval        None
 *
 * @note 
 */
void SPI_StructInit(SPI_ConfigStruct_T* configStruct)
{
    configStruct->direction = SPI_DIRECTION_2LINES_FULLDUPLEX;
    configStruct->mode = SPI_MODE_SLAVE;
    configStruct->dataSize = SPI_DATASIZE_8B;
    configStruct->cpol = SPI_CPOL_LOW;
    configStruct->cpha = SPI_CPHA_1EDGE;
    configStruct->nss = SPI_NSS_HARD;
    configStruct->baudRatePrescaler = SPI_BAUDRATE_PRESCALER_2;
    configStruct->firstBit = SPI_FIRSTBIT_MSB;
    configStruct->crcPolynomial = 7;
}

/*!
 * @brief        Fills each I2S_ConfigStruct_T member with its default value
 *
 * @param        configStruct: pointer to a SPI_ConfigStruct_T structure which will be initialized
 *
 * @retval        None
 *
 * @note 
 */
void I2S_StructInit(I2S_ConfigStruct_T* configStruct)
{
	configStruct->mode = I2S_MODE_SLAVE_TX;
	configStruct->standard = I2S_STANDARD_PHILLIPS;
	configStruct->dataFormat = I2S_DATAFORMAT_16b;
	configStruct->MCLKOutput = I2S_MCLK_OUTPUT_DISABLE;
	configStruct->audioFreq = I2S_AUDIO_FREQUENCY_Default;
	configStruct->CPOL = I2S_CPOL_LOW;
}
/*!
 * @brief        Enables the specified SPI peripheral
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_Enable(SPI_T* spi)
{
    spi->CTRL1_B.SPIEN = BIT_SET;
}

/*!
 * @brief        Disable the specified SPI peripheral
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_Disable(SPI_T* spi)
{
    spi->CTRL1_B.SPIEN = BIT_RESET;
}

/*!
 * @brief        Enables the specified I2S peripheral
 *
 * @param        spi: Select the I2S or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void I2S_Enable(SPI_T* spi)
{
    spi->I2SCTRL_B.I2SEN = BIT_SET;
}

/*!
 * @brief        Disable the specified I2S peripheral
 *
 * @param        spi: Select the I2S or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void I2S_Disable(SPI_T* spi)
{
    spi->I2SCTRL_B.I2SEN = BIT_RESET;
}

/*!
 * @brief        Enables the specified SPI/I2S interrupts.
 *
 * @param        spi: Select the I2S or the SPI peripheral
 *               SPI_I2S_Int : SPI/I2S interrupts
 *
 * @retval        None
 *
 * @note 
 */
void SPI_I2S_Int_Enable(SPI_T* spi, SPI_I2S_INT_T SPI_I2S_Int)
{
	if(SPI_I2S_Int == SPI_I2S_INT_TXBEIE)
	{
		spi->CTRL2_B.TXBEIE = ENABLE;
	}
	else if(SPI_I2S_Int == SPI_I2S_INT_RXBNEIE)
	{
		spi->CTRL2_B.RXBNEIE = ENABLE;
	}else if(SPI_I2S_Int == SPI_I2S_INT_ERR)
	{
		spi->CTRL2_B.ERRIE = ENABLE;
	}
}

/*!
 * @brief        Disables the specified SPI/I2S interrupts.
 *
 * @param        spi: Select the I2S or the SPI peripheral
 *               SPI_I2S_Int : SPI/I2S interrupts
 *
 * @retval        None
 *
 * @note 
 */
void SPI_I2S_Int_Disable(SPI_T* spi, SPI_I2S_INT_T SPI_I2S_Int)
{
	if(SPI_I2S_Int == SPI_I2S_INT_TXBEIE)
	{
		spi->CTRL2_B.TXBEIE = DISABLE;
	}
	else if(SPI_I2S_Int == SPI_I2S_INT_RXBNEIE)
	{
		spi->CTRL2_B.RXBNEIE = DISABLE;
	}else if(SPI_I2S_Int == SPI_I2S_INT_ERR)
	{
		spi->CTRL2_B.ERRIE = DISABLE;
	}
}

/*!
 * @brief        Enables the SPIx/I2Sx DMA interface.
 *
 * @param        spi: Select the I2S or the SPI peripheral
 *
 *               spi_i2s_DMAReq: SPI_I2S_DMAReq_TX or SPI_I2S_DMAReq_RX
 * @retval        None
 *
 * @note 
 */
void SPI_I2S_DMAEnable(SPI_T* spi, SPI_I2S_DMAReq spi_i2s_DMAReq)
{
	if(spi_i2s_DMAReq == SPI_I2S_DMAReq_TX)
	{
		spi->CTRL2_B.TX_DMAEN = ENABLE;
	}
	else
	{
		spi->CTRL2_B.RX_DMAEN = ENABLE;
	}
}

/*!
 * @brief        Disables the SPIx/I2Sx DMA interface.
 *
 * @param        spi: Select the I2S or the SPI peripheral
 *               spi_i2s_DMAReq: SPI_I2S_DMAReq_TX or SPI_I2S_DMAReq_RX
 *
 * @retval        None
 *
 * @note 
 */
void SPI_I2S_DMADisable(SPI_T* spi, SPI_I2S_DMAReq spi_i2s_DMAReq)
{
	if(spi_i2s_DMAReq == SPI_I2S_DMAReq_TX)
	{
		spi->CTRL2_B.TX_DMAEN = DISABLE;
	}
	else
	{
		spi->CTRL2_B.RX_DMAEN = DISABLE;
	}
}

/*!
 * @brief        Transmits a Data through the SPIx/I2Sx peripheral.
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *               data : Data to be transmitted.
 *
 * @retval        None
 *
 * @note 
 */
void SPI_I2S_SendData(SPI_T* spi, uint16_t data)
{
	spi->DATA = data;
}

/*!
 * @brief        Returns the most recent received data by the SPIx/I2Sx peripheral.
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval       data :The value of the received data.
 *
 * @note 
 */
uint16_t SPI_I2S_ReceiveData(SPI_T* spi)
{
	return spi->DATA;
}

/*!
 * @brief        Set the SPI NSS internal by Software
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_NSSInternalSoftwareSet(SPI_T* spi)
{
    spi->CTRL1_B.ISS = BIT_SET;
}

/*!
 * @brief        Reset the SPI NSS internal by Software
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_NSSInternalSoftwareReset(SPI_T* spi)
{
    spi->CTRL1_B.ISS = BIT_RESET;
}

/*!
 * @brief        Enables the specified SPI SS output
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_SSOutputEnable(SPI_T* spi)
{
    spi->CTRL2_B.SSOEN = BIT_SET;
}

/*!
 * @brief        Disable the specified SPI SS output
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_SSOutputDisable(SPI_T* spi)
{
    spi->CTRL2_B.SSOEN = BIT_RESET;
}

/*!
 * @brief        Configures the specified SPI data size
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @param        dataSize: Select the SPI data Size
 *
 * @retval        None
 *
 * @note 
 */
void SPI_DataSizeConfig(SPI_T* spi, uint16_t dataSize)
{
    spi->CTRL1 &= (uint16_t)~SPI_DATASIZE_16B;
    spi->CTRL1 |= dataSize;
}

/*!
 * @brief        Enables the specified SPI CRC transmission
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_TransmitCRC_Enable(SPI_T* spi)
{
    spi->CTRL1_B.CRCNXT = BIT_SET;
}

/*!
 * @brief        Enables the specified SPI CRC value calculation of the transferred bytes
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_CalculateCRC_Enable(SPI_T* spi)
{
    spi->CTRL1_B.CRCEN = BIT_SET;
}

/*!
 * @brief        Disable the specified SPI CRC value calculation of the transferred bytes
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        None
 *
 * @note 
 */
void SPI_CalculateCRC_Disable(SPI_T* spi)
{
    spi->CTRL1_B.CRCEN = BIT_RESET;
}

/*!
 * @brief        Reads the specified SPI transmit CRC register value
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        The SPI transmit CRC register value
 *
 * @note 
 */
uint16_t SPI_ReadCRC_TX(SPI_T* spi)
{
    return spi->TXCRC_B.TXCRC;
}

/*!
 * @brief        Reads the specified SPI receive CRC register value
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        The SPI receive CRC register value
 *
 * @note 
 */
uint16_t SPI_ReadCRC_RX(SPI_T* spi)
{
    return spi->RXCRC_B.RXCRC;
}

/*!
 * @brief        Reads the specified SPI CRC Polynomial register value
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @retval        The SPI CRC Polynomial register value
 *
 * @note 
 */
uint16_t SPI_ReadCRCPolynomial(SPI_T* spi)
{
    return spi->CRCPOLY_B.CRCPOLY;
}

/*!
 * @brief        Configures the specified SPI data transfer direction
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @param        direction: Select the SPI data transfer direction
 *
 * @retval        None
 *
 * @note 
 */
void SPI_BiDirectionalLineConfig(SPI_T* spi, uint16_t direction)
{
    if(direction == SPI_DIRECTION_TX)
    {
        spi->CTRL1 |= SPI_DIRECTION_TX;
    }
    else
    {
        spi->CTRL1 &= SPI_DIRECTION_RX;
    }
}

/*!
 * @brief        Checks whether the specified SPI/I2S flag is set or not.
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @param        flag: specifies the SPI/I2S flag to check
 *
 * @retval        SET or RESET
 *
 * @note 
 */
uint16_t SPI_I2S_ReadFlag(SPI_T* spi, SPI_FLAG_T flag)
{
	if((spi->STS & flag) != RESET)
	{
		return SET;
	}
	else
	{
		return RESET;
	}
}

/*!
 * @brief        Clears the SPIx CRC Error (CRCERR) flag.
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @param        flag: specifies the SPI flag to clear.
 *
 * @retval        None
 *
 * @note          This function clears only CRCERR flag.
 */
void SPI_I2S_ClearFlag(SPI_T* spi, SPI_FLAG_T flag)
{
	spi->STS_B.CRCEF = BIT_RESET;
}

/*!
 * @brief        Checks whether the specified SPI/I2S interrupt has occurred or not.
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @param        interrupt: specifies the SPI/I2S interrupt source to check.
 *
 * @retval       SET or RESET
 *
 * @note          
 */
uint16_t SPI_I2S_ReadIntFlag(SPI_T* spi, SPI_I2S_INT_T interrupt)
{
	if(interrupt == SPI_I2S_INT_TXBEIE)
	{
		return spi->CTRL2_B.TXBEIE;
	}
	else if(interrupt == SPI_I2S_INT_RXBNEIE)
	{
		return spi->CTRL2_B.RXBNEIE;
	}
	else if(interrupt == SPI_I2S_INT_RXOF)
	{
		return spi->STS_B.RXOF;
	}
	else if(interrupt == SPI_INT_MEF)
	{
		return spi->STS_B.MEF;
	}
	else if(interrupt == SPI_INT_CRCEF)
	{
		return spi->STS_B.CRCEF;
	}
	else 
	{
		/*return I2S_INT_UDF*/
		return spi->STS_B.UDF;
	}
}

/*!
 * @brief        Clears the SPIx CRC Error (CRCERR) interrupt pending bit.
 *
 * @param        spi: Select the SPI or the SPI peripheral
 *
 * @param        interrupt:  specifies the SPI interrupt pending bit to clear.
 *
 * @retval       None
 *
 * @note        This function clears only CRCERR interrupt pending bit.  
 */
void SPI_I2S_ClearIntFlag(SPI_T* spi, SPI_I2S_INT_T interrupt)
{
	spi->STS_B.CRCEF = BIT_RESET;
}
